

/* -------------------------------------------------------------------------------- */
/* -- uGUI - Generic GUI module (C)Achim D鯾ler, 2015								-- */
/* -------------------------------------------------------------------------------- */
// uGUI is a generic GUI module for embedded systems.
// This is a free software that is open for education, research and commercial
// developments under license policy of following terms.
//
//	Copyright (C) 2015, Achim D鯾ler, all rights reserved.
//	URL: http://www.embeddedlightning.com/
//
// * The uGUI module is a free software and there is NO WARRANTY.
// * No restriction on use. You can use, modify and redistribute it for
//	 personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
// * Redistributions of source code must retain the above copyright notice.
//

/* -------------------------------------------------------------------------------- */
/* -- REVISION HISTORY															 -- */
/* -------------------------------------------------------------------------------- */
//	Mar 18, 2015	V0.3	Driver support added.
//						Window and object support added.
//						Touch support added.
//						Fixed some minor bugs.
//
//	Oct 20, 2014	V0.2	Function UG_DrawRoundFrame() added.
//						Function UG_FillRoundFrame() added.
//						Function UG_DrawArc() added.
//						Fixed some minor bugs.
//
//	Oct 11, 2014	V0.1	First release.

/* -------------------------------------------------------------------------------- */
#include "ugui.h"
#include "ugui_draw.h"
#include <stdio.h>


/* Static functions */
UG_RESULT _UG_WindowDrawTitle(UG_WINDOW * wnd);
void _UG_WindowUpdate(UG_WINDOW * wnd);
UG_RESULT _UG_WindowClear(UG_WINDOW * wnd);
void _UG_ButtonUpdate(UG_WINDOW * wnd, UG_OBJECT * obj);
void _UG_TextboxUpdate(UG_WINDOW * wnd, UG_OBJECT * obj);
void _UG_CheckboxUpdate(UG_WINDOW * wnd, UG_OBJECT * obj);
void _UG_ImageUpdate(UG_WINDOW * wnd, UG_OBJECT * obj);
void _UG_PutChar(char chr, UG_S16 x, UG_S16 y, UG_COLOR fc, UG_COLOR bc, const UG_FONT * font);
bool _UG_UpdateSwitch(UG_U8 direction);

/* Pointer to the g_ugui */
UG_GUI * g_ugui;


UG_RESULT UG_Init(UG_GUI * g, malloc_buffer_callback cb)
{
	UG_U8 i;

	g->has_update = false;
	g->w_dim = TFT_WIDTH;
	g->h_dim = TFT_HEIGHT;
	g->console.a.xs = 4;
	g->console.a.ys = 4;
	g->console.a.xe = g->w_dim - g->console.a.xs - 1;
	g->console.a.ye = g->h_dim - g->console.a.xs - 1;
	g->console.x_pos = g->console.a.xe;
	g->console.y_pos = g->console.a.ye;
	g->console.fore_color = C_BLUE;
	g->console.back_color = C_DEF_DESKTOP;
	g->char_h_space = 1;
	g->char_v_space = 1;
	g->font = DEFAULT_UG_FONT;

	g->desktop_color = C_DEF_DESKTOP;
	g->fore_color = C_WHITE;
	g->back_color = C_BLACK;
	g->next_window = NULL;
	g->active_window = NULL;
	g->last_window = NULL;

	/* Clear drivers */
	for(i = 0; i < NUMBER_OF_DRIVERS; i++) {
		g->driver[i].driver = NULL;
		g->driver[i].state = DRIVER_INVALID;
	}

	g->driver[DRIVER_MALLOC_BUFFER].driver = cb;
	g->driver[DRIVER_MALLOC_BUFFER].state = DRIVER_REGISTERED;

#if (								CONFIG_UI_BUFFER == UI_BUFFER_ONE)
	g->g_current_fbuffer =
		 ((malloc_buffer_callback) g->driver[DRIVER_MALLOC_BUFFER].driver) (TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);

	memset(g->g_current_fbuffer, 0, TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);
#elif ( 							CONFIG_UI_BUFFER == UI_BUFFER_THREE)
	g->g_temp_fbuffer =
		 ((malloc_buffer_callback) g->driver[DRIVER_MALLOC_BUFFER].driver) (TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);
	g->g_prev_fbuffer =
		 ((malloc_buffer_callback) g->driver[DRIVER_MALLOC_BUFFER].driver) (TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);
	g->g_mid_fbuffer = 
		 ((malloc_buffer_callback) g->driver[DRIVER_MALLOC_BUFFER].driver) (TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);
	g->g_next_fbuffer =
		 ((malloc_buffer_callback) g->driver[DRIVER_MALLOC_BUFFER].driver) (TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);

	memset(g->g_temp_fbuffer, 0, TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);
	memset(g->g_prev_fbuffer, 0, TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);
	memset(g->g_mid_fbuffer, 0, TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);
	memset(g->g_next_fbuffer, 0, TFT_WIDTH * TFT_HEIGHT * COLOR_BPP_SIZE);

	g->g_current_fbuffer = g->g_mid_fbuffer;
	g->switching = false;
#endif

	g_ugui = g;

	return UG_RESULT_OK;
}


void UG_SelectGUI(UG_GUI * g)
{
	g_ugui = g;
}


void UG_FontSelect(const UG_FONT * font)
{
	g_ugui->font = *font;
}


void UG_FillScreen(UG_COLOR c)
{
	UG_FillFrame(0, 0, g_ugui->w_dim - 1, g_ugui->h_dim - 1, c);
}


void UG_FillFrame(UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c)
{
    UG_S16 n;

	if(x2 < x1) {
		n = x2;
		x2 = x1;
		x1 = n;
	}

	if(y2 < y1) {
		n = y2;
		y2 = y1;
		y1 = n;
	}

	/* Is hardware acceleration available? */
	draw_frame_driver(x1, y1, x2, y2, c);
}


void UG_FillRoundFrame(UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_S16 r, UG_COLOR c)
{
	UG_S16 x, y, xd;

	if(x2 < x1) {
		x = x2;
		x2 = x1;
		x1 = x;
	}

	if(y2 < y1) {
		y = y2;
		y2 = y1;
		y1 = y;
	}

	if(r <= 0)
		return;

	xd = 3 - (r << 1);
	x = 0;
	y = r;

	UG_FillFrame(x1 + r, y1, x2 - r, y2, c);

	while(x <= y) {
		if(y > 0) {
			UG_DrawLine(x2 + x - r, y1 - y + r, x2 + x - r, y + y2 - r, c);
			UG_DrawLine(x1 - x + r, y1 - y + r, x1 - x + r, y + y2 - r, c);
		}

		if(x > 0) {
			UG_DrawLine(x1 - y + r, y1 - x + r, x1 - y + r, x + y2 - r, c);
			UG_DrawLine(x2 + y - r, y1 - x + r, x2 + y - r, x + y2 - r, c);
		}

		if(xd < 0) {
			xd += (x << 2) + 6;
		}
		else {
			xd += ((x - y) << 2) + 10;
			y--;
		}

		x++;
	}
}


void UG_DrawMesh(UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c)
{
	UG_S16 n, m;

	if(x2 < x1) {
		n = x2;
		x2 = x1;
		x1 = n;
	}

	if(y2 < y1) {
		n = y2;
		y2 = y1;
		y1 = n;
	}

	for(m = y1; m <= y2; m += 2) {
		for(n = x1; n <= x2; n += 2) {
			draw_pixel_driver(n, m, c);
		}
	}
}


void UG_DrawFrame(UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c)
{
	UG_DrawLine(x1, y1, x2, y1, c);
	UG_DrawLine(x1, y2, x2, y2, c);
	UG_DrawLine(x1, y1, x1, y2, c);
	UG_DrawLine(x2, y1, x2, y2, c);
}


void UG_DrawRoundFrame(UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_S16 r, UG_COLOR c)
{
	UG_S16 n;

	if(x2 < x1) {
		n = x2;
		x2 = x1;
		x1 = n;
	}

	if(y2 < y1) {
		n = y2;
		y2 = y1;
		y1 = n;
	}

	if(r > x2)
		return;

	if(r > y2)
		return;

	UG_DrawLine(x1 + r, y1, x2 - r, y1, c);
	UG_DrawLine(x1 + r, y2, x2 - r, y2, c);
	UG_DrawLine(x1, y1 + r, x1, y2 - r, c);
	UG_DrawLine(x2, y1 + r, x2, y2 - r, c);
	UG_DrawArc(x1 + r, y1 + r, r, 0x0C, c);
	UG_DrawArc(x2 - r, y1 + r, r, 0x03, c);
	UG_DrawArc(x1 + r, y2 - r, r, 0x30, c);
	UG_DrawArc(x2 - r, y2 - r, r, 0xC0, c);
}


void UG_DrawPixel(UG_S16 x0, UG_S16 y0, UG_COLOR c)
{
	draw_pixel_driver(x0, y0, c);
}


void UG_DrawCircle(UG_S16 x0, UG_S16 y0, UG_S16 r, UG_COLOR c)
{
	UG_S16 x, y, xd, yd, e;

	if(x0 < 0)
		return;

	if(y0 < 0)
		return;

	if(r <= 0)
		return;

	xd = 1 - (r << 1);
	yd = 0;
	e = 0;
	x = r;
	y = 0;

	while(x >= y) {
		draw_pixel_driver(x0 - x, y0 + y, c);
		draw_pixel_driver(x0 - x, y0 - y, c);
		draw_pixel_driver(x0 + x, y0 + y, c);
		draw_pixel_driver(x0 + x, y0 - y, c);
		draw_pixel_driver(x0 - y, y0 + x, c);
		draw_pixel_driver(x0 - y, y0 - x, c);
		draw_pixel_driver(x0 + y, y0 + x, c);
		draw_pixel_driver(x0 + y, y0 - x, c);

		y++;
		e += yd;
		yd += 2;

		if(((e << 1) +xd) > 0) {
			x--;
			e += xd;
			xd += 2;
		}
	}
}


void UG_FillCircle(UG_S16 x0, UG_S16 y0, UG_S16 r, UG_COLOR c)
{
	UG_S16 x, y, xd;

	if(x0 < 0)
		return;

	if(y0 < 0)
		return;

	if(r <= 0)
		return;

	xd = 3 - (r << 1);
	x = 0;
	y = r;

	while(x <= y) {
		if(y > 0) {
			UG_DrawLine(x0 - x, y0 - y, x0 - x, y0 + y, c);
			UG_DrawLine(x0 + x, y0 - y, x0 + x, y0 + y, c);
		}

		if(x > 0) {
			UG_DrawLine(x0 - y, y0 - x, x0 - y, y0 + x, c);
			UG_DrawLine(x0 + y, y0 - x, x0 + y, y0 + x, c);
		}

		if(xd < 0) {
			xd += (x << 2) + 6;
		}
		else {
			xd += ((x - y) << 2) + 10;
			y--;
		}

		x++;
	}

	UG_DrawCircle(x0, y0, r, c);
}


void UG_DrawArc(UG_S16 x0, UG_S16 y0, UG_S16 r, UG_U8 s, UG_COLOR c)
{
	UG_S16 x, y, xd, yd, e;

	if(x0 < 0)
		return;

	if(y0 < 0)
		return;

	if(r <= 0)
		return;

	xd = 1 - (r << 1);
	yd = 0;
	e = 0;
	x = r;
	y = 0;

	while(x >= y) {
		// Q1
		if(s & 0x01)
			draw_pixel_driver(x0 + x, y0 - y, c);

		if(s & 0x02)
			draw_pixel_driver(x0 + y, y0 - x, c);

		// Q2
		if(s & 0x04)
			draw_pixel_driver(x0 - y, y0 - x, c);

		if(s & 0x08)
			draw_pixel_driver(x0 - x, y0 - y, c);

		// Q3
		if(s & 0x10)
			draw_pixel_driver(x0 - x, y0 + y, c);

		if(s & 0x20)
			draw_pixel_driver(x0 - y, y0 + x, c);

		// Q4
		if(s & 0x40)
			draw_pixel_driver(x0 + y, y0 + x, c);

		if(s & 0x80)
			draw_pixel_driver(x0 + x, y0 + y, c);

		y++;
		e += yd;
		yd += 2;

		if(((e << 1) +xd) > 0) {
			x--;
			e += xd;
			xd += 2;
		}
	}
}


void UG_DrawLine(UG_S16 x1, UG_S16 y1, UG_S16 x2, UG_S16 y2, UG_COLOR c)
{
	/* Is hardware acceleration available? */
	draw_line_driver(x1, y1, x2, y2, c);
}


void UG_PutString(UG_S16 x, UG_S16 y, char * str)
{
	UG_S16 xp, yp;
	UG_U8 cw;
	char chr;

	xp = x;
	yp = y;

	while(*str != 0) {
		chr = *str++;

		if(chr < g_ugui->font.start_char || chr > g_ugui->font.end_char)
			continue;

		if(chr == '\n') {
			xp = g_ugui->w_dim;
			continue;
		}

		cw = g_ugui->font.widths ? g_ugui->font.widths[chr - g_ugui->font.start_char]: g_ugui->font.char_width;

		if(xp + cw > g_ugui->w_dim - 1) {
			xp = x;
			yp += g_ugui->font.char_height + g_ugui->char_v_space;
		}

		UG_PutChar(chr, xp, yp, g_ugui->fore_color, g_ugui->back_color);

		xp += cw + g_ugui->char_h_space;
	}
}


void UG_PutChar(char chr, UG_S16 x, UG_S16 y, UG_COLOR fc, UG_COLOR bc)
{
	_UG_PutChar(chr, x, y, fc, bc, &g_ugui->font);
}


void UG_ConsolePutString(char * str)
{
	char chr;
	UG_U8 cw;

	while(*str != 0) {
		chr = *str;

		if(chr == '\n') {
			g_ugui->console.x_pos = g_ugui->w_dim;
			str++;
			continue;
		}

		cw = g_ugui->font.widths ? g_ugui->font.widths[chr - g_ugui->font.start_char]: g_ugui->font.char_width;
		g_ugui->console.x_pos += cw + g_ugui->char_h_space;

		if(g_ugui->console.x_pos + cw > g_ugui->console.a.xe) {
			g_ugui->console.x_pos = g_ugui->console.a.xs;
			g_ugui->console.y_pos += g_ugui->font.char_height + g_ugui->char_v_space;
		}

		if(g_ugui->console.y_pos + g_ugui->font.char_height > g_ugui->console.a.ye) {
			g_ugui->console.x_pos = g_ugui->console.a.xs;
			g_ugui->console.y_pos = g_ugui->console.a.ys;
			UG_FillFrame(g_ugui->console.a.xs, g_ugui->console.a.ys, g_ugui->console.a.xe, g_ugui->console.a.ye, 
				g_ugui->console.back_color);
		}

		UG_PutChar(chr, g_ugui->console.x_pos, g_ugui->console.y_pos, g_ugui->console.fore_color, g_ugui->console.back_color);
		str++;
	}
}


void UG_ConsoleSetArea(UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye)
{
	g_ugui->console.a.xs = xs;
	g_ugui->console.a.ys = ys;
	g_ugui->console.a.xe = xe;
	g_ugui->console.a.ye = ye;
}


void UG_ConsoleSetForecolor(UG_COLOR c)
{
	g_ugui->console.fore_color = c;
}


void UG_ConsoleSetBackcolor(UG_COLOR c)
{
	g_ugui->console.back_color = c;
}


void UG_SetForecolor(UG_COLOR c)
{
	g_ugui->fore_color = c;
}


void UG_SetBackcolor(UG_COLOR c)
{
	g_ugui->back_color = c;
}


UG_S16 UG_GetXDim(void)
{
	return g_ugui->w_dim;
}


UG_S16 UG_GetYDim(void)
{
	return g_ugui->h_dim;
}


void UG_FontSetHSpace(UG_U16 s)
{
	g_ugui->char_h_space = s;
}


void UG_FontSetVSpace(UG_U16 s)
{
	g_ugui->char_v_space = s;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const UG_COLOR pal_window[] =
{
	/* Frame 0 */
	COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 

		/* Frame 1 */
	COLOR_CONVERT(0xFFFFFF), 
		COLOR_CONVERT(0xFFFFFF), 
		COLOR_CONVERT(0x696969), 
		COLOR_CONVERT(0x696969), 

		/* Frame 2 */
	COLOR_CONVERT(0xE3E3E3), 
		COLOR_CONVERT(0xE3E3E3), 
		COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 
		};


const UG_COLOR pal_button_pressed[] =
{
	/* Frame 0 */
	COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 

		/* Frame 1 */
	COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 

		/* Frame 2 */
	COLOR_CONVERT(0xF0F0F0), 
		COLOR_CONVERT(0xF0F0F0), 
		COLOR_CONVERT(0xF0F0F0), 
		COLOR_CONVERT(0xF0F0F0), 
		};


const UG_COLOR pal_button_released[] =
{
	/* Frame 0 */
	COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 

		/* Frame 1 */
	COLOR_CONVERT(0xFFFFFF), 
		COLOR_CONVERT(0xFFFFFF), 
		COLOR_CONVERT(0x696969), 
		COLOR_CONVERT(0x696969), 

		/* Frame 2 */
	COLOR_CONVERT(0xE3E3E3), 
		COLOR_CONVERT(0xE3E3E3), 
		COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 
		};


const UG_COLOR pal_checkbox_pressed[] =
{
	/* Frame 0 */
	COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 

		/* Frame 1 */
	COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 

		/* Frame 2 */
	COLOR_CONVERT(0xF0F0F0), 
		COLOR_CONVERT(0xF0F0F0), 
		COLOR_CONVERT(0xF0F0F0), 
		COLOR_CONVERT(0xF0F0F0), 
		};


const UG_COLOR pal_checkbox_released[] =
{
	/* Frame 0 */
	COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 
		COLOR_CONVERT(0x646464), 

		/* Frame 1 */
	COLOR_CONVERT(0xFFFFFF), 
		COLOR_CONVERT(0xFFFFFF), 
		COLOR_CONVERT(0x696969), 
		COLOR_CONVERT(0x696969), 

		/* Frame 2 */
	COLOR_CONVERT(0xE3E3E3), 
		COLOR_CONVERT(0xE3E3E3), 
		COLOR_CONVERT(0xA0A0A0), 
		COLOR_CONVERT(0xA0A0A0), 
		};


/* -------------------------------------------------------------------------------- */
/* -- INTERNAL FUNCTIONS														 -- */
/* -------------------------------------------------------------------------------- */
void _UG_PutChar(char chr, UG_S16 x, UG_S16 y, UG_COLOR fc, UG_COLOR bc, const UG_FONT * font)
{
	UG_U16 i, j, k, xo, yo, c, bn, actual_char_width;
	UG_U8 b, bt;
	UG_U32 index;
	UG_COLOR color;

	bt = (UG_U8)
	chr;

	if(bt < font->start_char || bt > font->end_char)
		return;

	yo = y;
	bn = font->char_width;

	if(!bn)
		return;

	bn >>= 3;

	if(font->char_width % 8)
		bn++;

	actual_char_width = (font->widths ? font->widths[bt - font->start_char]: font->char_width);

	printf("-------:%d\r\n", font->font_type);

	/*Not accelerated output*/
	if(font->font_type == FONT_TYPE_1BPP) {
		index = (bt - font->start_char) *font->char_height * bn;

		for(j = 0; j < font->char_height; j++) {
			xo = x;
			c = actual_char_width;

			for(i = 0; i < bn; i++) {
				b = font->p[index++];

				for(k = 0; (k < 8) && c; k++) {
					if(b & 0x01) {
						draw_pixel_driver(xo, yo, fc);
					}
					else {
						//draw_pixel_driver(xo, yo, bc);
					}

					b >>= 1;
					xo++;
					c--;
				}
			}

			yo++;
		}
	}
	else if(font->font_type == FONT_TYPE_8BPP) {
		index = (bt - font->start_char) *font->char_height * font->char_width;

		for(j = 0; j < font->char_height; j++) {
			xo = x;

			for(i = 0; i < actual_char_width; i++) {
				b = font->p[index++];
				color = ((((fc & 0xFF) *b + (bc & 0xFF) * (256 - b)) >> 8) & 0xFF) | //Blue component
				((((fc & 0xFF00) *b + (bc & 0xFF00) * (256 - b)) >> 8) & 0xFF00) | //Green component
				((((fc & 0xFF0000) *b + (bc & 0xFF0000) * (256 - b)) >> 8) & 0xFF0000); //Red component
				draw_pixel_driver(xo, yo, fc);
				xo++;
			}

			index += font->char_width - actual_char_width;
			yo++;
		}
	}
}


void _UG_PutText(UG_TEXT * txt)
{
	UG_U16 sl, rc, wl;
	UG_S16 xp, yp;
	UG_S16 xs = txt->a.xs;
	UG_S16 ys = txt->a.ys;
	UG_S16 xe = txt->a.xe;
	UG_S16 ye = txt->a.ye;
	UG_U8 align = txt->align;
	UG_S16 char_width = txt->font->char_width;
	UG_S16 char_height = txt->font->char_height;
	UG_S16 char_h_space = txt->h_space;
	UG_S16 char_v_space = txt->v_space;

	char chr;

	char * str = txt->str;
	char * c = str;

	if(txt->font->p == NULL)
		return;

	if(str == NULL)
		return;

	if((ye - ys) < txt->font->char_height)
		return;

	rc = 1;
	c = str;

	while(*c != 0) {
		if(*c == '\n')
			rc++;

		c++;
	}

	yp = 0;

	if(align & (ALIGN_V_CENTER | ALIGN_V_BOTTOM)) {
		yp = ye - ys + 1;
		yp -= char_height * rc;
		yp -= char_v_space * (rc - 1);

		if(yp < 0)
			return;
	}

	if(align & ALIGN_V_CENTER)
		yp >>= 1;

	yp += ys;

	while(1) {
		sl = 0;
		c = str;
		wl = 0;

		while((*c != 0) && (*c != '\n')) {
			if(*c < txt->font->start_char || *c > txt->font->end_char) {
				c++;
				continue;
			}

			sl++;
			wl += (txt->font->widths ? txt->font->widths[*c - txt->font->start_char]: char_width) +char_h_space;
			c++;
		}

		wl -= char_h_space;

		xp = xe - xs + 1;
		xp -= wl;

		if(xp < 0)
			return;

		if(align & ALIGN_H_LEFT)
			xp = 0;
		else if(align & ALIGN_H_CENTER)
			xp >>= 1;

		xp += xs;

		while((*str != '\n')) {
			chr = *str++;

			if(chr == 0)
				return;

			_UG_PutChar(chr, xp, yp, txt->fc, txt->bc, txt->font);
			xp += (txt->font->widths ? txt->font->widths[chr - txt->font->start_char]: char_width) +char_h_space;
		}

		str++;
		yp += char_height + char_v_space;
	}
}


UG_OBJECT * _UG_GetFreeObject(UG_WINDOW * wnd)
{
	UG_U8 i;
	UG_OBJECT * obj = (UG_OBJECT *)
	wnd->objlst;

	for(i = 0; i < wnd->objcnt; i++) {
		obj = (UG_OBJECT *) (&wnd->objlst[i]);

		if((obj->state & OBJ_STATE_FREE) && (obj->state & OBJ_STATE_VALID)) {
			/* Free object found! */
			return obj;
		}
	}

	return NULL;
}


UG_OBJECT * _UG_SearchObject(UG_WINDOW * wnd, UG_U8 type, UG_U8 id)
{
	UG_U8 i;
	UG_OBJECT * obj = (UG_OBJECT *)
	wnd->objlst;

	for(i = 0; i < wnd->objcnt; i++) {
		obj = (UG_OBJECT *) (&wnd->objlst[i]);

		if(! (obj->state & OBJ_STATE_FREE) && (obj->state & OBJ_STATE_VALID)) {
			if((obj->type == type) && (obj->id == id)) {
				/* Requested object found! */
				return obj;
			}
		}
	}

	return NULL;
}


UG_RESULT _UG_DeleteObject(UG_WINDOW * wnd, UG_U8 type, UG_U8 id)
{
	UG_OBJECT * obj = NULL;

	obj = _UG_SearchObject(wnd, type, id);

	/* Object found? */
	if(obj != NULL) {
		/* We dont't want to delete a visible or busy object! */
		if((obj->state & OBJ_STATE_VISIBLE) || (obj->state & OBJ_STATE_UPDATE))
			return UG_RESULT_FAIL;

		obj->state = OBJ_STATE_INIT;
		obj->data = NULL;
		obj->event = 0;
		obj->id = 0;
		obj->touch_state = 0;
		obj->type = 0;
		obj->update = NULL;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


void _UG_ProcessTouchData(UG_WINDOW * wnd)
{
	UG_S16 xp, yp;
	UG_U16 i, objcnt;
	UG_OBJECT * obj;
	UG_U8 objstate;
	UG_U8 objtouch;
	UG_U8 tchstate;

	xp = g_ugui->touch.xp;
	yp = g_ugui->touch.yp;
	tchstate = g_ugui->touch.state;

	objcnt = wnd->objcnt;

	for(i = 0; i < objcnt; i++) {
		obj = (UG_OBJECT *) &wnd->objlst[i];
		objstate = obj->state;
		objtouch = obj->touch_state;

		if(! (objstate & OBJ_STATE_FREE) && (objstate & OBJ_STATE_VALID) && (objstate & OBJ_STATE_VISIBLE) &&
			 ! (objstate & OBJ_STATE_REDRAW)) {
			/* Process touch data */
			if((tchstate) && xp != -1) {
				if(! (objtouch & OBJ_TOUCH_STATE_IS_PRESSED)) {
					objtouch |= OBJ_TOUCH_STATE_PRESSED_OUTSIDE_OBJECT | OBJ_TOUCH_STATE_CHANGED;
					objtouch &=
						 ~(OBJ_TOUCH_STATE_RELEASED_ON_OBJECT | OBJ_TOUCH_STATE_RELEASED_OUTSIDE_OBJECT | OBJ_TOUCH_STATE_CLICK_ON_OBJECT);
				}

				objtouch &= ~OBJ_TOUCH_STATE_IS_PRESSED_ON_OBJECT;

				if(xp >= obj->a_abs.xs) {
					if(xp <= obj->a_abs.xe) {
						if(yp >= obj->a_abs.ys) {
							if(yp <= obj->a_abs.ye) {
								objtouch |= OBJ_TOUCH_STATE_IS_PRESSED_ON_OBJECT;

								if(! (objtouch & OBJ_TOUCH_STATE_IS_PRESSED)) {
									objtouch &= ~OBJ_TOUCH_STATE_PRESSED_OUTSIDE_OBJECT;
									objtouch |= OBJ_TOUCH_STATE_PRESSED_ON_OBJECT;
								}
							}
						}
					}
				}

				objtouch |= OBJ_TOUCH_STATE_IS_PRESSED;
			}
			else if(objtouch & OBJ_TOUCH_STATE_IS_PRESSED) {
				if(objtouch & OBJ_TOUCH_STATE_IS_PRESSED_ON_OBJECT) {
					if(objtouch & OBJ_TOUCH_STATE_PRESSED_ON_OBJECT)
						objtouch |= OBJ_TOUCH_STATE_CLICK_ON_OBJECT;

					objtouch |= OBJ_TOUCH_STATE_RELEASED_ON_OBJECT;
				}
				else {
					objtouch |= OBJ_TOUCH_STATE_RELEASED_OUTSIDE_OBJECT;
				}

				if(objtouch & OBJ_TOUCH_STATE_IS_PRESSED) {
					objtouch |= OBJ_TOUCH_STATE_CHANGED;
				}

				objtouch &=
					 ~(OBJ_TOUCH_STATE_PRESSED_OUTSIDE_OBJECT | OBJ_TOUCH_STATE_PRESSED_ON_OBJECT | OBJ_TOUCH_STATE_IS_PRESSED);
			}
		}

		obj->touch_state = objtouch;
	}
}


void _UG_UpdateObjects(UG_WINDOW * wnd)
{
	UG_U16 i, objcnt;
	UG_OBJECT * obj;
	UG_U8 objstate;
	UG_U8 objtouch;

	/* Check each object, if it needs to be updated? */
	objcnt = wnd->objcnt;

	for(i = 0; i < objcnt; i++) {
		obj = (UG_OBJECT *) &wnd->objlst[i];
		objstate = obj->state;
		objtouch = obj->touch_state;

		if(! (objstate & OBJ_STATE_FREE) && (objstate & OBJ_STATE_VALID)) {
			if(objstate & OBJ_STATE_UPDATE) {
				g_ugui->has_update = true;

				obj->update(wnd, obj);
			}
			else if((objstate & OBJ_STATE_VISIBLE) && (objstate & OBJ_STATE_TOUCH_ENABLE)) {
				if((objtouch & (OBJ_TOUCH_STATE_CHANGED | OBJ_TOUCH_STATE_IS_PRESSED))) {
					g_ugui->has_update = true;

					obj->update(wnd, obj);
				}
			}
		}
	}
}


void _UG_HandleEvents(UG_WINDOW * wnd)
{
	UG_U16 i, objcnt;
	UG_OBJECT * obj;
	UG_U8 objstate;
	static UG_MESSAGE msg;

	msg.src = NULL;

	/* Handle window-related events */
	//ToDo

	/* Handle object-related events */
	msg.type = MSG_TYPE_OBJECT;
	objcnt = wnd->objcnt;

	for(i = 0; i < objcnt; i++) {
		obj = (UG_OBJECT *) &wnd->objlst[i];
		objstate = obj->state;

		if(! (objstate & OBJ_STATE_FREE) && (objstate & OBJ_STATE_VALID)) {
			if(obj->event != OBJ_EVENT_NONE) {
				msg.src = &obj;
				msg.id = obj->type;
				msg.sub_id = obj->id;
				msg.event = obj->event;

				wnd->cb(&msg);

				obj->event = OBJ_EVENT_NONE;
			}
		}
	}
}


void _UG_DrawObjectFrame(UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye, UG_COLOR * p)
{
	// Frame 0
	UG_DrawLine(xs, ys, xe - 1, ys, *p++);
	UG_DrawLine(xs, ys + 1, xs, ye - 1, *p++);
	UG_DrawLine(xs, ye, xe, ye, *p++);
	UG_DrawLine(xe, ys, xe, ye - 1, *p++);

	// Frame 1
	UG_DrawLine(xs + 1, ys + 1, xe - 2, ys + 1, *p++);
	UG_DrawLine(xs + 1, ys + 2, xs + 1, ye - 2, *p++);
	UG_DrawLine(xs + 1, ye - 1, xe - 1, ye - 1, *p++);
	UG_DrawLine(xe - 1, ys + 1, xe - 1, ye - 2, *p++);

	// Frame 2
	UG_DrawLine(xs + 2, ys + 2, xe - 3, ys + 2, *p++);
	UG_DrawLine(xs + 2, ys + 3, xs + 2, ye - 3, *p++);
	UG_DrawLine(xs + 2, ye - 2, xe - 2, ye - 2, *p++);
	UG_DrawLine(xe - 2, ys + 2, xe - 2, ye - 3, *p);
}


#ifdef USE_PRERENDER_EVENT
void _UG_SendObjectPrerenderEvent(UG_WINDOW * wnd, UG_OBJECT * obj)
{
	UG_MESSAGE msg;

	msg.event = OBJ_EVENT_PRERENDER;
	msg.type = MSG_TYPE_OBJECT;
	msg.id = obj->type;
	msg.sub_id = obj->id;
	msg.src = obj;

	wnd->cb(&msg);
}


#endif

#ifdef USE_POSTRENDER_EVENT
void _UG_SendObjectPostrenderEvent(UG_WINDOW * wnd, UG_OBJECT * obj)
{
	UG_MESSAGE msg;

	msg.event = OBJ_EVENT_POSTRENDER;
	msg.type = MSG_TYPE_OBJECT;
	msg.id = obj->type;
	msg.sub_id = obj->id;
	msg.src = obj;

	wnd->cb(&msg);
}


#endif

/* -------------------------------------------------------------------------------- */
/* -- DRIVER FUNCTIONS															 -- */
/* -------------------------------------------------------------------------------- */
void UG_DriverRegister(UG_U8 type, void * driver)
{
	if(type >= NUMBER_OF_DRIVERS)
		return;

	g_ugui->driver[type].driver = driver;
	g_ugui->driver[type].state = DRIVER_REGISTERED;
}


/* -------------------------------------------------------------------------------- */
/* -- MISCELLANEOUS FUNCTIONS													 -- */
/* -------------------------------------------------------------------------------- */
void UG_Update(void)
{
	UG_WINDOW * wnd;

#if (								CONFIG_UI_BUFFER == UI_BUFFER_NONE)

	if(g_ugui->driver[DRIVER_UPDATE_EVNET].state & DRIVER_REGISTERED)
		((update_event_callback) g_ugui->driver[DRIVER_UPDATE_EVNET].driver) (UG_UPDATE_START);

#endif

	/* Is somebody waiting for this update? */
	if(g_ugui->state & UG_SATUS_WAIT_FOR_UPDATE)
		g_ugui->state &= ~UG_SATUS_WAIT_FOR_UPDATE;

	/* Keep track of the windows */
	if(g_ugui->next_window != g_ugui->active_window) {
		if(g_ugui->next_window != NULL) {
			if(g_ugui->active_window == NULL)
				g_ugui->active_window = g_ugui->next_window;

			g_ugui->last_window = g_ugui->active_window;
			g_ugui->active_window = g_ugui->next_window;

			/* Do we need to draw an inactive title? */
			if((g_ugui->last_window->style & WND_STYLE_SHOW_TITLE) &&
				 (g_ugui->last_window->state & WND_STATE_VISIBLE)) {
				/* Do both windows differ in size */
				if((g_ugui->last_window->a.xs != g_ugui->active_window->a.xs) ||
					 (g_ugui->last_window->a.xe != g_ugui->active_window->a.xe) ||
					 (g_ugui->last_window->a.ys != g_ugui->active_window->a.ys) ||
					 (g_ugui->last_window->a.ye != g_ugui->active_window->a.ye)) {
					/* Redraw title of the last window */
					_UG_WindowDrawTitle(g_ugui->last_window);
				}
			}

			g_ugui->active_window->state &= ~WND_STATE_REDRAW_TITLE;
			g_ugui->active_window->state |= WND_STATE_UPDATE | WND_STATE_VISIBLE;
		}
	}

	/* Is there an active window */
	if(g_ugui->active_window != NULL) {
		wnd = g_ugui->active_window;

		/* Does the window need to be updated? */
		if(wnd->state & WND_STATE_UPDATE) {
			g_ugui->has_update = true;

			/* Do it! */
			_UG_WindowUpdate(wnd);
		}

		/* Is the window visible? */
		if(wnd->state & WND_STATE_VISIBLE) {
			_UG_ProcessTouchData(wnd);
			_UG_UpdateObjects(wnd);
			_UG_HandleEvents(wnd);
		}
	}

#if (								CONFIG_UI_BUFFER == UI_BUFFER_NONE)

	if(g_ugui->driver[DRIVER_UPDATE_EVNET].state & DRIVER_REGISTERED)
		((update_event_callback) g_ugui->driver[DRIVER_UPDATE_EVNET].driver) (UG_UPDATE_END);

#else
	if(g_ugui->switching) {
		g_ugui->has_update = true;

		g_ugui->switching = _UG_UpdateSwitch(g_ugui->direction);
	}

	if(g_ugui->has_update) {
		g_ugui->has_update = false;

		if(g_ugui->driver[DRIVER_UPDATE_BUFFER].state & DRIVER_REGISTERED)
			((update_buffer_callback) g_ugui->driver[DRIVER_UPDATE_BUFFER].driver) (g_ugui->g_current_fbuffer, TFT_WIDTH, TFT_HEIGHT);
	}

#endif
}

bool UG_SwitchPanel(UG_U8 direction)
{
	g_ugui->direction = direction;
	g_ugui->switching = true;

	memcpy(g_ugui->g_temp_fbuffer, g_ugui->g_current_fbuffer, TFT_HEIGHT * TFT_WIDTH << 1);
	g_ugui->g_current_fbuffer = g_ugui->g_temp_fbuffer;
}

bool _UG_UpdateSwitch(UG_U8 direction)
{
	static UG_U16 offset = 0;
	static UG_U16 prev_size, next_size, row_size;

	offset += 50;
	if(offset >= 320) {
		offset = 0;
		g_ugui->g_current_fbuffer = g_ugui->g_next_fbuffer;
		return false;
	}

#if (CONFIG_GUI_COLOR == USE_COLOR_RGB565)
	UG_U8 *p_prev = g_ugui->g_mid_fbuffer + (offset << 1);
	UG_U8 *p_curr = g_ugui->g_temp_fbuffer;
	UG_U8 *p_next = g_ugui->g_next_fbuffer;

	prev_size = (TFT_WIDTH - offset) << 1;
	next_size = offset << 1;
#elif (CONFIG_GUI_COLOR == USE_COLOR_RGB888)
	UG_U8 *p_prev = g_ugui->g_mid_fbuffer + (offset * 3);
	UG_U8 *p_curr = g_ugui->g_mid_fbuffer;
	UG_U8 *p_next = g_ugui->g_mid_fbuffer;

	prev_size = (TFT_WIDTH - offset) * 3;
	next_size = row_size - prev_size;
#elif (CONFIG_GUI_COLOR == USE_COLOR_ARGB8888)
	UG_U8 *p_prev = g_ugui->g_mid_fbuffer + (offset << 2);
	UG_U8 *p_curr = g_ugui->g_mid_fbuffer;
	UG_U8 *p_next = g_ugui->g_mid_fbuffer;

	prev_size = (TFT_WIDTH - offset) << 2;
	next_size = offset << 2;
#endif

	if(direction) {
		UG_U16 x, y;

		for(y = 0; y < TFT_HEIGHT; ++y) {
			//
			memcpy(p_curr, p_prev, prev_size);
			p_curr += prev_size;

			//
			memcpy(p_curr, p_next, next_size);
			p_curr += next_size;

			p_prev += (prev_size + next_size);
			p_next += (prev_size + next_size);
		}
	}

	return true;
}

void UG_WaitForUpdate(void)
{
	g_ugui->state |= UG_SATUS_WAIT_FOR_UPDATE;

#ifdef USE_MULTITASKING

	while((volatile UG_U8)
	g_ugui->state & UG_SATUS_WAIT_FOR_UPDATE) {
	};

#else

	while((UG_U8)
	g_ugui->state & UG_SATUS_WAIT_FOR_UPDATE) {
	};

#endif
}


void UG_DrawBMP(UG_S16 xp, UG_S16 yp, UG_BMP * bmp)
{
	UG_S16 x, y, xs;
	UG_U8 r, g, b;
	UG_U16 * p;
	UG_U16 tmp;
	UG_COLOR c;

	if(bmp->p == NULL)
		return;

	/* Only support 16 BPP so far */
	if(bmp->bpp == BMP_BPP_16) {
		p = (UG_U16 *)
		bmp->p;
	}
	else {
		return;
	}

	xs = xp;

	for(y = 0; y < bmp->height; y++) {
		xp = xs;

		for(x = 0; x < bmp->width; x++) {
			tmp = *p++;

			/* Convert RGB565 to RGB888 */
			r = (tmp >> 11) & 0x1F;
			r <<= 3;
			g = (tmp >> 5) & 0x3F;
			g <<= 2;
			b = (tmp) & 0x1F;
			b <<= 3;
			c = ((UG_COLOR) r << 16) | ((UG_COLOR) g << 8) | (UG_COLOR)
			b;
			UG_DrawPixel(xp++, yp, c);
		}

		yp++;
	}
}


void UG_TouchUpdate(UG_S16 xp, UG_S16 yp, UG_U8 state)
{
	g_ugui->touch.xp = xp;
	g_ugui->touch.yp = yp;
	g_ugui->touch.state = state;
}


/* -------------------------------------------------------------------------------- */
/* -- WINDOW FUNCTIONS															 -- */
/* -------------------------------------------------------------------------------- */
UG_RESULT UG_WindowCreate(UG_WINDOW * wnd, UG_OBJECT * objlst, UG_U8 objcnt, void(*cb) (UG_MESSAGE *))
{
	UG_U8 i;
	UG_OBJECT * obj = NULL;

	if((wnd == NULL) || (objlst == NULL) || (objcnt == 0))
		return UG_RESULT_FAIL;

	/* Initialize all objects of the window */
	for(i = 0; i < objcnt; i++) {
		obj = (UG_OBJECT *) &objlst[i];
		obj->state = OBJ_STATE_INIT;
		obj->data = NULL;
	}

	/* Initialize window */
	wnd->objcnt = objcnt;
	wnd->objlst = objlst;
	wnd->state = WND_STATE_VALID;
	wnd->fc = C_DEF_WND_FC;
	wnd->bc = C_DEF_WND_BC;
	wnd->a.xs = 0;
	wnd->a.ys = 0;
	wnd->a.xe = UG_GetXDim() - 1;
	wnd->a.ye = UG_GetYDim() - 1;
	wnd->cb = cb;
	wnd->style = WND_STYLE_2D | WND_STYLE_HIDE_TITLE;

	/* Initialize window title-bar */
	wnd->title.str = NULL;
	wnd->title.font = &g_ugui->font;
	wnd->title.h_space = 2;
	wnd->title.v_space = 2;
	wnd->title.align = ALIGN_CENTER_RIGHT;
	wnd->title.fc = C_WHITE;
	wnd->title.bc = C_LIGHT_BLUE;
	wnd->title.ifc = C_WHITE;
	wnd->title.ibc = C_GRAY;
	wnd->title.height = 15;

	return UG_RESULT_OK;
}


UG_RESULT UG_WindowDelete(UG_WINDOW * wnd)
{
	if(wnd == g_ugui->active_window)
		return UG_RESULT_FAIL;

	/* Only delete valid windows */
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->state = WND_STATE_INVALID;
		wnd->cb = NULL;
		wnd->objcnt = 0;
		wnd->objlst = NULL;
		wnd->a.xs = 0;
		wnd->a.ys = 0;
		wnd->a.xe = 0;
		wnd->a.ye = 0;
		wnd->style = 0;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowShow(UG_WINDOW * wnd)
{
	if(wnd != NULL) {
		/* Force an update, even if this is the active window! */
		wnd->state |= WND_STATE_VISIBLE | WND_STATE_UPDATE;
		wnd->state &= ~WND_STATE_REDRAW_TITLE;
		g_ugui->next_window = wnd;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowHide(UG_WINDOW * wnd)
{
	if(wnd != NULL) {
		if(wnd == g_ugui->active_window) {
			/* Is there an old window which just lost the focus? */
			if((g_ugui->last_window != NULL) && (g_ugui->last_window->state & WND_STATE_VISIBLE)) {
				if((g_ugui->last_window->a.xs > wnd->a.xs) || (g_ugui->last_window->a.ys > wnd->a.ys) ||
					 (g_ugui->last_window->a.xe < wnd->a.xe) || (g_ugui->last_window->a.ye < wnd->a.ye)) {
					_UG_WindowClear(wnd);
				}

				g_ugui->next_window = g_ugui->last_window;
			}
			else {
				g_ugui->active_window->state &= ~WND_STATE_VISIBLE;
				g_ugui->active_window->state |= WND_STATE_UPDATE;
			}
		}
		else {
			/* If the old window is visible, clear it! */
			_UG_WindowClear(wnd);
		}

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowResize(UG_WINDOW * wnd, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye)
{
	UG_S16 pos;
	UG_S16 xmax, ymax;

	xmax = UG_GetXDim() - 1;
	ymax = UG_GetYDim() - 1;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		/* Do some checks... */
		if((xs < 0) || (ys < 0))
			return UG_RESULT_FAIL;

		if((xe > xmax) || (ye > ymax))
			return UG_RESULT_FAIL;

		pos = xe - xs;

		if(pos < 10)
			return UG_RESULT_FAIL;

		pos = ye - ys;

		if(pos < 10)
			return UG_RESULT_FAIL;

		/* ... and if everything is OK move the window! */
		wnd->a.xs = xs;
		wnd->a.ys = ys;
		wnd->a.xe = xe;
		wnd->a.ye = ye;

		if((wnd->state & WND_STATE_VISIBLE) && (g_ugui->active_window == wnd)) {
			if(wnd->a.ys)
				UG_FillFrame(0, 0, xmax, wnd->a.ys - 1, g_ugui->desktop_color);

			pos = wnd->a.ye + 1;

			if(! (pos > ymax))
				UG_FillFrame(0, pos, xmax, ymax, g_ugui->desktop_color);

			if(wnd->a.xs)
				UG_FillFrame(0, wnd->a.ys, wnd->a.xs - 1, wnd->a.ye, g_ugui->desktop_color);

			pos = wnd->a.xe + 1;

			if(! (pos > xmax))
				UG_FillFrame(pos, wnd->a.ys, xmax, wnd->a.ye, g_ugui->desktop_color);

			wnd->state &= ~WND_STATE_REDRAW_TITLE;
			wnd->state |= WND_STATE_UPDATE;
		}

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowAlert(UG_WINDOW * wnd)
{
	UG_COLOR c;

	c = UG_WindowGetTitleTextColor(wnd);

	if(UG_WindowSetTitleTextColor(wnd, UG_WindowGetTitleColor(wnd)) == UG_RESULT_FAIL)
		return UG_RESULT_FAIL;

	if(UG_WindowSetTitleColor(wnd, c) == UG_RESULT_FAIL)
		return UG_RESULT_FAIL;

	return UG_RESULT_OK;
}


UG_RESULT UG_WindowSetForeColor(UG_WINDOW * wnd, UG_COLOR fc)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->fc = fc;
		wnd->state |= WND_STATE_UPDATE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetBackColor(UG_WINDOW * wnd, UG_COLOR bc)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->bc = bc;
		wnd->state |= WND_STATE_UPDATE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleTextColor(UG_WINDOW * wnd, UG_COLOR c)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.fc = c;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleColor(UG_WINDOW * wnd, UG_COLOR c)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.bc = c;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleInactiveTextColor(UG_WINDOW * wnd, UG_COLOR c)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.ifc = c;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleInactiveColor(UG_WINDOW * wnd, UG_COLOR c)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.ibc = c;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleText(UG_WINDOW * wnd, char * str)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.str = str;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleTextFont(UG_WINDOW * wnd, const UG_FONT * font)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		wnd->title.font = font;

		if(wnd->title.height <= (font->char_height + 1)) {
			wnd->title.height = font->char_height + 2;
			wnd->state &= ~WND_STATE_REDRAW_TITLE;
		}

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleTextHSpace(UG_WINDOW * wnd, UG_S8 hs)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.h_space = hs;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleTextVSpace(UG_WINDOW * wnd, UG_S8 vs)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.v_space = vs;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleTextAlignment(UG_WINDOW * wnd, UG_U8 align)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.align = align;
		wnd->state |= WND_STATE_UPDATE | WND_STATE_REDRAW_TITLE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetTitleHeight(UG_WINDOW * wnd, UG_U8 height)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->title.height = height;
		wnd->state &= ~WND_STATE_REDRAW_TITLE;
		wnd->state |= WND_STATE_UPDATE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetXStart(UG_WINDOW * wnd, UG_S16 xs)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->a.xs = xs;

		if(UG_WindowResize(wnd, wnd->a.xs, wnd->a.ys, wnd->a.xe, wnd->a.ye) == UG_RESULT_FAIL)
			return UG_RESULT_FAIL;

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetYStart(UG_WINDOW * wnd, UG_S16 ys)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->a.ys = ys;

		if(UG_WindowResize(wnd, wnd->a.xs, wnd->a.ys, wnd->a.xe, wnd->a.ye) == UG_RESULT_FAIL)
			return UG_RESULT_FAIL;

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetXEnd(UG_WINDOW * wnd, UG_S16 xe)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->a.xe = xe;

		if(UG_WindowResize(wnd, wnd->a.xs, wnd->a.ys, wnd->a.xe, wnd->a.ye) == UG_RESULT_FAIL)
			return UG_RESULT_FAIL;

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetYEnd(UG_WINDOW * wnd, UG_S16 ye)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		wnd->a.ye = ye;

		if(UG_WindowResize(wnd, wnd->a.xs, wnd->a.ys, wnd->a.xe, wnd->a.ye) == UG_RESULT_FAIL)
			return UG_RESULT_FAIL;

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_RESULT UG_WindowSetStyle(UG_WINDOW * wnd, UG_U8 style)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		/* 3D or 2D? */
		if(style & WND_STYLE_3D) {
			wnd->style |= WND_STYLE_3D;
		}
		else {
			wnd->style &= ~WND_STYLE_3D;
		}

		/* Show title-bar? */
		if(style & WND_STYLE_SHOW_TITLE) {
			wnd->style |= WND_STYLE_SHOW_TITLE;
		}
		else {
			wnd->style &= ~WND_STYLE_SHOW_TITLE;
		}

		wnd->state |= WND_STATE_UPDATE;
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_COLOR UG_WindowGetForeColor(UG_WINDOW * wnd)
{
	UG_COLOR c = C_BLACK;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		c = wnd->fc;
	}

	return c;
}


UG_COLOR UG_WindowGetBackColor(UG_WINDOW * wnd)
{
	UG_COLOR c = C_BLACK;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		c = wnd->bc;
	}

	return c;
}


UG_COLOR UG_WindowGetTitleTextColor(UG_WINDOW * wnd)
{
	UG_COLOR c = C_BLACK;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		c = wnd->title.fc;
	}

	return c;
}


UG_COLOR UG_WindowGetTitleColor(UG_WINDOW * wnd)
{
	UG_COLOR c = C_BLACK;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		c = wnd->title.bc;
	}

	return c;
}


UG_COLOR UG_WindowGetTitleInactiveTextColor(UG_WINDOW * wnd)
{
	UG_COLOR c = C_BLACK;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		c = wnd->title.ifc;
	}

	return c;
}


UG_COLOR UG_WindowGetTitleInactiveColor(UG_WINDOW * wnd)
{
	UG_COLOR c = C_BLACK;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		c = wnd->title.ibc;
	}

	return c;
}


char * UG_WindowGetTitleText(UG_WINDOW * wnd)
{
	char * str = NULL;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		str = wnd->title.str;
	}

	return str;
}


UG_FONT * UG_WindowGetTitleTextFont(UG_WINDOW * wnd)
{
	UG_FONT * f = NULL;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		f = (UG_FONT *)
		wnd->title.font;
	}

	return f;
}


UG_S8 UG_WindowGetTitleTextHSpace(UG_WINDOW * wnd)
{
	UG_S8 hs = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		hs = wnd->title.h_space;
	}

	return hs;
}


UG_S8 UG_WindowGetTitleTextVSpace(UG_WINDOW * wnd)
{
	UG_S8 vs = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		vs = wnd->title.v_space;
	}

	return vs;
}


UG_U8 UG_WindowGetTitleTextAlignment(UG_WINDOW * wnd)
{
	UG_U8 align = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		align = wnd->title.align;
	}

	return align;
}


UG_U8 UG_WindowGetTitleHeight(UG_WINDOW * wnd)
{
	UG_U8 h = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		h = wnd->title.height;
	}

	return h;
}


UG_S16 UG_WindowGetXStart(UG_WINDOW * wnd)
{
	UG_S16 xs = -1;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		xs = wnd->a.xs;
	}

	return xs;
}


UG_S16 UG_WindowGetYStart(UG_WINDOW * wnd)
{
	UG_S16 ys = -1;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		ys = wnd->a.ys;
	}

	return ys;
}


UG_S16 UG_WindowGetXEnd(UG_WINDOW * wnd)
{
	UG_S16 xe = -1;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		xe = wnd->a.xe;
	}

	return xe;
}


UG_S16 UG_WindowGetYEnd(UG_WINDOW * wnd)
{
	UG_S16 ye = -1;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		ye = wnd->a.ye;
	}

	return ye;
}


UG_U8 UG_WindowGetStyle(UG_WINDOW * wnd)
{
	UG_U8 style = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		style = wnd->style;
	}

	return style;
}


UG_RESULT UG_WindowGetArea(UG_WINDOW * wnd, UG_AREA * a)
{
	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		a->xs = wnd->a.xs;
		a->ys = wnd->a.ys;
		a->xe = wnd->a.xe;
		a->ye = wnd->a.ye;

		if(wnd->style & WND_STYLE_3D) {
			a->xs += 3;
			a->ys += 3;
			a->xe -= 3;
			a->ye -= 3;
		}

		if(wnd->style & WND_STYLE_SHOW_TITLE) {
			a->ys += wnd->title.height + 1;
		}

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


UG_S16 UG_WindowGetInnerWidth(UG_WINDOW * wnd)
{
	UG_S16 w = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		w = wnd->a.xe - wnd->a.xs;

		/* 3D style? */
		if(wnd->style & WND_STYLE_3D)
			w -= 6;

		if(w < 0)
			w = 0;
	}

	return w;
}


UG_S16 UG_WindowGetOuterWidth(UG_WINDOW * wnd)
{
	UG_S16 w = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		w = wnd->a.xe - wnd->a.xs;

		if(w < 0)
			w = 0;
	}

	return w;
}


UG_S16 UG_WindowGetInnerHeight(UG_WINDOW * wnd)
{
	UG_S16 h = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		h = wnd->a.ye - wnd->a.ys;

		/* 3D style? */
		if(wnd->style & WND_STYLE_3D)
			h -= 6;

		/* Is the title active */
		if(wnd->style & WND_STYLE_SHOW_TITLE)
			h -= wnd->title.height;

		if(h < 0)
			h = 0;
	}

	return h;
}


UG_S16 UG_WindowGetOuterHeight(UG_WINDOW * wnd)
{
	UG_S16 h = 0;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		h = wnd->a.ye - wnd->a.ys;

		if(h < 0)
			h = 0;
	}

	return h;
}


UG_RESULT _UG_WindowDrawTitle(UG_WINDOW * wnd)
{
	UG_TEXT txt;
	UG_S16 xs, ys, xe, ye;

	if((wnd != NULL) && (wnd->state & WND_STATE_VALID)) {
		xs = wnd->a.xs;
		ys = wnd->a.ys;
		xe = wnd->a.xe;
		ye = wnd->a.ye;

		/* 3D style? */
		if(wnd->style & WND_STYLE_3D) {
			xs += 3;
			ys += 3;
			xe -= 3;
			ye -= 3;
		}

		/* Is the window active or inactive? */
		if(wnd == g_ugui->active_window) {
			txt.bc = wnd->title.bc;
			txt.fc = wnd->title.fc;
		}
		else {
			txt.bc = wnd->title.ibc;
			txt.fc = wnd->title.ifc;
		}

		/* Draw title */
		UG_FillFrame(xs, ys, xe, ys + wnd->title.height - 1, txt.bc);

		/* Draw title text */
		txt.str = wnd->title.str;
		txt.font = wnd->title.font;
		txt.a.xs = xs + 3;
		txt.a.ys = ys;
		txt.a.xe = xe;
		txt.a.ye = ys + wnd->title.height - 1;
		txt.align = wnd->title.align;
		txt.h_space = wnd->title.h_space;
		txt.v_space = wnd->title.v_space;
		_UG_PutText(&txt);

		/* Draw line */
		UG_DrawLine(xs, ys + wnd->title.height, xe, ys + wnd->title.height, pal_window[11]);
		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


void _UG_WindowUpdate(UG_WINDOW * wnd)
{
	UG_U16 i, objcnt;
	UG_OBJECT * obj;
	UG_S16 xs, ys, xe, ye;

	xs = wnd->a.xs;
	ys = wnd->a.ys;
	xe = wnd->a.xe;
	ye = wnd->a.ye;

	wnd->state &= ~WND_STATE_UPDATE;

	/* Is the window visible? */
	if(wnd->state & WND_STATE_VISIBLE) {
		/* 3D style? */
		if((wnd->style & WND_STYLE_3D) && ! (wnd->state & WND_STATE_REDRAW_TITLE)) {
			_UG_DrawObjectFrame(xs, ys, xe, ye, (UG_COLOR *) pal_window);
			xs += 3;
			ys += 3;
			xe -= 3;
			ye -= 3;
		}

		/* Show title bar? */
		if(wnd->style & WND_STYLE_SHOW_TITLE) {
			_UG_WindowDrawTitle(wnd);
			ys += wnd->title.height + 1;

			if(wnd->state & WND_STATE_REDRAW_TITLE) {
				wnd->state &= ~WND_STATE_REDRAW_TITLE;
				return;
			}
		}

		/* Draw window area? */
		UG_FillFrame(xs, ys, xe, ye, wnd->bc);

		/* Force each object to be updated! */
		objcnt = wnd->objcnt;

		for(i = 0; i < objcnt; i++) {
			obj = (UG_OBJECT *) &wnd->objlst[i];

			if(! (obj->state & OBJ_STATE_FREE) && (obj->state & OBJ_STATE_VALID) && (obj->state & OBJ_STATE_VISIBLE))
				obj->state |= (OBJ_STATE_UPDATE | OBJ_STATE_REDRAW);
		}
	}
	else {
		UG_FillFrame(wnd->a.xs, wnd->a.xs, wnd->a.xe, wnd->a.ye, g_ugui->desktop_color);
	}
}


UG_RESULT _UG_WindowClear(UG_WINDOW * wnd)
{
	if(wnd != NULL) {
		if(wnd->state & WND_STATE_VISIBLE) {
			wnd->state &= ~WND_STATE_VISIBLE;
			UG_FillFrame(wnd->a.xs, wnd->a.ys, wnd->a.xe, wnd->a.ye, g_ugui->desktop_color);

			if(wnd != g_ugui->active_window) {
				/* If the current window is visible, update it! */
				if(g_ugui->active_window->state & WND_STATE_VISIBLE) {
					g_ugui->active_window->state &= ~WND_STATE_REDRAW_TITLE;
					g_ugui->active_window->state |= WND_STATE_UPDATE;
				}
			}
		}

		return UG_RESULT_OK;
	}

	return UG_RESULT_FAIL;
}


/* -------------------------------------------------------------------------------- */
/* -- BUTTON FUNCTIONS															 -- */
/* -------------------------------------------------------------------------------- */
UG_RESULT UG_ButtonCreate(UG_WINDOW * wnd, UG_BUTTON * btn, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye)
{
	UG_OBJECT * obj;

	obj = _UG_GetFreeObject(wnd);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	/* Initialize object-specific parameters */
	btn->state = BTN_STATE_RELEASED;
	btn->bc = wnd->bc;
	btn->fc = wnd->fc;
	btn->abc = wnd->bc;
	btn->afc = wnd->fc;
	btn->style = BTN_STYLE_2D;
	btn->align = ALIGN_CENTER;
	btn->font = &g_ugui->font;
	btn->str = "hello";

	/* Initialize standard object parameters */
	obj->update = _UG_ButtonUpdate;
	obj->touch_state = OBJ_TOUCH_STATE_INIT;
	obj->type = OBJ_TYPE_BUTTON;
	obj->event = OBJ_EVENT_NONE;
	obj->a_rel.xs = xs;
	obj->a_rel.ys = ys;
	obj->a_rel.xe = xe;
	obj->a_rel.ye = ye;
	obj->a_abs.xs = -1;
	obj->a_abs.ys = -1;
	obj->a_abs.xe = -1;
	obj->a_abs.ye = -1;
	obj->id = id;
	obj->state |= OBJ_STATE_VISIBLE | OBJ_STATE_REDRAW | OBJ_STATE_VALID | OBJ_STATE_TOUCH_ENABLE;
	obj->data = (void *)
	btn;

	/* Update function: Do your thing! */
	obj->state &= ~OBJ_STATE_FREE;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonDelete(UG_WINDOW * wnd, UG_U8 id)
{
	return _UG_DeleteObject(wnd, OBJ_TYPE_BUTTON, id);
}


UG_RESULT UG_ButtonShow(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	obj->state |= OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonHide(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);

	btn->state &= ~BTN_STATE_PRESSED;
	obj->touch_state = OBJ_TOUCH_STATE_INIT;
	obj->event = OBJ_EVENT_NONE;
	obj->state &= ~OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetForeColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR fc)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->fc = fc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetBackColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR bc)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->bc = bc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetAlternateForeColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR afc)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->afc = afc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetAlternateBackColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR abc)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->abc = abc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetText(UG_WINDOW * wnd, UG_U8 id, char * str)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->str = str;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetFont(UG_WINDOW * wnd, UG_U8 id, const UG_FONT * font)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->font = font;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetStyle(UG_WINDOW * wnd, UG_U8 id, UG_U8 style)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);

	/* Select color scheme */
	btn->style &=
		 ~(BTN_STYLE_USE_ALTERNATE_COLORS | BTN_STYLE_TOGGLE_COLORS | BTN_STYLE_NO_BORDERS | BTN_STYLE_NO_FILL);
	btn->state |= BTN_STATE_ALWAYS_REDRAW;

	if(style & BTN_STYLE_NO_BORDERS) {
		btn->style |= BTN_STYLE_NO_BORDERS;
	}

	if(style & BTN_STYLE_NO_FILL) {
		btn->style |= BTN_STYLE_NO_FILL;
	}

	if(style & BTN_STYLE_TOGGLE_COLORS) {
		btn->style |= BTN_STYLE_TOGGLE_COLORS;
	}
	else if(style & BTN_STYLE_USE_ALTERNATE_COLORS) {
		btn->style |= BTN_STYLE_USE_ALTERNATE_COLORS;
	}
	else {
		btn->state &= ~BTN_STATE_ALWAYS_REDRAW;
	}

	/* 3D or 2D */
	if(style & BTN_STYLE_3D) {
		btn->style |= BTN_STYLE_3D;
	}
	else {
		btn->style &= ~BTN_STYLE_3D;
	}

	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetHSpace(UG_WINDOW * wnd, UG_U8 id, UG_S8 hs)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->h_space = hs;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetVSpace(UG_WINDOW * wnd, UG_U8 id, UG_S8 vs)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->v_space = vs;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_ButtonSetAlignment(UG_WINDOW * wnd, UG_U8 id, UG_U8 align)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_BUTTON *) (obj->data);
	btn->align = align;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_COLOR UG_ButtonGetForeColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		c = btn->fc;
	}

	return c;
}


UG_COLOR UG_ButtonGetBackColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		c = btn->bc;
	}

	return c;
}


UG_COLOR UG_ButtonGetAlternateForeColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		c = btn->afc;
	}

	return c;
}


UG_COLOR UG_ButtonGetAlternateBackColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		c = btn->abc;
	}

	return c;
}


char * UG_ButtonGetText(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	char * str = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		str = btn->str;
	}

	return str;
}


UG_FONT * UG_ButtonGetFont(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_FONT * font = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		font = (UG_FONT *)
		btn->font;
	}

	return font;
}


UG_U8 UG_ButtonGetStyle(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_U8 style = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		style = btn->style;
	}

	return style;
}


UG_S8 UG_ButtonGetHSpace(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_S8 hs = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		hs = btn->h_space;
	}

	return hs;
}


UG_S8 UG_ButtonGetVSpace(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_S8 vs = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		vs = btn->v_space;
	}

	return vs;
}


UG_U8 UG_ButtonGetAlignment(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_BUTTON * btn = NULL;
	UG_U8 align = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_BUTTON, id);

	if(obj != NULL) {
		btn = (UG_BUTTON *) (obj->data);
		align = btn->align;
	}

	return align;
}


void _UG_ButtonUpdate(UG_WINDOW * wnd, UG_OBJECT * obj)
{
	UG_BUTTON * btn;
	UG_AREA a;
	UG_TEXT txt;
	UG_U8 d;

	/* Get object-specific data */
	btn = (UG_BUTTON *) (obj->data);

	/* -------------------------------------------------- */
	/* Object touch section 								*/
	/* -------------------------------------------------- */
	if((obj->touch_state & OBJ_TOUCH_STATE_CHANGED)) {
		/* Handle 'click' event */
		if(obj->touch_state & OBJ_TOUCH_STATE_CLICK_ON_OBJECT) {
			obj->event = BTN_EVENT_CLICKED;
			obj->state |= OBJ_STATE_UPDATE;
		}

		/* Is the button pressed down? */
		if(obj->touch_state & OBJ_TOUCH_STATE_PRESSED_ON_OBJECT) {
			btn->state |= BTN_STATE_PRESSED;
			obj->state |= OBJ_STATE_UPDATE;
			obj->event = OBJ_EVENT_PRESSED;
		}

		/* Can we release the button? */
		else if(btn->state & BTN_STATE_PRESSED) {
			btn->state &= ~BTN_STATE_PRESSED;
			obj->state |= OBJ_STATE_UPDATE;
			obj->event = OBJ_EVENT_RELEASED;
		}

		obj->touch_state &= ~OBJ_TOUCH_STATE_CHANGED;
	}

	/* -------------------------------------------------- */
	/* Object update section								*/
	/* -------------------------------------------------- */
	if(obj->state & OBJ_STATE_UPDATE) {
		if(obj->state & OBJ_STATE_VISIBLE) {
			/* Full redraw necessary? */
			if((obj->state & OBJ_STATE_REDRAW) || (btn->state & BTN_STATE_ALWAYS_REDRAW)) {
				UG_WindowGetArea(wnd, &a);
				obj->a_abs.xs = obj->a_rel.xs + a.xs;
				obj->a_abs.ys = obj->a_rel.ys + a.ys;
				obj->a_abs.xe = obj->a_rel.xe + a.xs;
				obj->a_abs.ye = obj->a_rel.ye + a.ys;

				if(obj->a_abs.ye > wnd->a.ye)
					return;

				if(obj->a_abs.xe > wnd->a.xe)
					return;

#ifdef USE_PRERENDER_EVENT
				_UG_SendObjectPrerenderEvent(wnd, obj);
#endif

				/* 3D or 2D style? */
				d = (btn->style & BTN_STYLE_3D) ? 3: 1;

				txt.bc = btn->bc;
				txt.fc = btn->fc;

				if(btn->state & BTN_STATE_PRESSED) {
					/* "toggle" style? */
					if(btn->style & BTN_STYLE_TOGGLE_COLORS) {
						/* Swap colors */
						txt.bc = btn->fc;
						txt.fc = btn->bc;
					}

					/* Use alternate colors? */
					else if(btn->style & BTN_STYLE_USE_ALTERNATE_COLORS) {
						txt.bc = btn->abc;
						txt.fc = btn->afc;
					}
				}

				if(! (btn->style & BTN_STYLE_NO_FILL))
					UG_FillFrame(obj->a_abs.xs + d, obj->a_abs.ys + d, obj->a_abs.xe - d, obj->a_abs.ye - d, txt.bc);

				/* Draw button text */
				txt.a.xs = obj->a_abs.xs + d;
				txt.a.ys = obj->a_abs.ys + d;
				txt.a.xe = obj->a_abs.xe - d;
				txt.a.ye = obj->a_abs.ye - d;
				txt.align = btn->align;
				txt.font = btn->font;
				txt.h_space = 2;
				txt.v_space = 2;
				txt.str = btn->str;
				_UG_PutText(&txt);
				obj->state &= ~OBJ_STATE_REDRAW;

#ifdef USE_POSTRENDER_EVENT
				_UG_SendObjectPostrenderEvent(wnd, obj);
#endif
			}

			/* Draw button frame */
			if(! (btn->style & BTN_STYLE_NO_BORDERS)) {
				if(btn->style & BTN_STYLE_3D) { /* 3D */
					_UG_DrawObjectFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xe, obj->a_abs.ye, 
						(btn->state & BTN_STATE_PRESSED) ? (UG_COLOR *) pal_button_pressed: (UG_COLOR *) pal_button_released);
				}
				else { /* 2D */
					UG_DrawFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xe, obj->a_abs.ye, 
						(btn->state & BTN_STATE_PRESSED) ? btn->abc: btn->afc);
				}
			}
		}
		else {
			if(! (btn->style & BTN_STYLE_NO_FILL))
				UG_FillFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xe, obj->a_abs.ye, wnd->bc);
		}

		obj->state &= ~OBJ_STATE_UPDATE;
	}
}


/* -------------------------------------------------------------------------------- */
/* -- Checkbox FUNCTIONS															 -- */
/* -------------------------------------------------------------------------------- */
UG_RESULT UG_CheckboxCreate(UG_WINDOW * wnd, UG_CHECKBOX * chb, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye)
{
	UG_OBJECT * obj;

	obj = _UG_GetFreeObject(wnd);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	/* Initialize object-specific parameters */
	chb->state = CHB_STATE_RELEASED;
	chb->bc = wnd->bc;
	chb->fc = wnd->fc;
	chb->abc = wnd->bc;
	chb->afc = wnd->fc;
	chb->style = CHB_STYLE_3D;
	chb->align = ALIGN_TOP_LEFT;

	if(g_ugui != NULL)
		chb->font = &g_ugui->font;
	else
		chb->font = NULL;

	chb->str = "-";
	chb->checked = 0;

	/* Initialize standard object parameters */
	obj->update = _UG_CheckboxUpdate;
	obj->touch_state = OBJ_TOUCH_STATE_INIT;
	obj->type = OBJ_TYPE_CHECKBOX;
	obj->event = OBJ_EVENT_NONE;
	obj->a_rel.xs = xs;
	obj->a_rel.ys = ys;
	obj->a_rel.xe = xe;
	obj->a_rel.ye = ye;
	obj->a_abs.xs = -1;
	obj->a_abs.ys = -1;
	obj->a_abs.xe = -1;
	obj->a_abs.ye = -1;
	obj->id = id;
	obj->state |= OBJ_STATE_VISIBLE | OBJ_STATE_REDRAW | OBJ_STATE_VALID | OBJ_STATE_TOUCH_ENABLE;
	obj->data = (void *)
	chb;

	/* Update function: Do your thing! */
	obj->state &= ~OBJ_STATE_FREE;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxDelete(UG_WINDOW * wnd, UG_U8 id)
{
	return _UG_DeleteObject(wnd, OBJ_TYPE_CHECKBOX, id);
}


UG_RESULT UG_CheckboxShow(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	obj->state |= OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxHide(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);

	btn->state &= ~CHB_STATE_PRESSED;
	obj->touch_state = OBJ_TOUCH_STATE_INIT;
	obj->event = OBJ_EVENT_NONE;
	obj->state &= ~OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetCheched(UG_WINDOW * wnd, UG_U8 id, UG_U8 ch)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->checked = ch;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetForeColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR fc)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->fc = fc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetBackColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR bc)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->bc = bc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetAlternateForeColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR afc)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->afc = afc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetAlternateBackColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR abc)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->abc = abc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetText(UG_WINDOW * wnd, UG_U8 id, char * str)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->str = str;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetFont(UG_WINDOW * wnd, UG_U8 id, const UG_FONT * font)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->font = font;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetStyle(UG_WINDOW * wnd, UG_U8 id, UG_U8 style)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * chk = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	chk = (UG_CHECKBOX *) (obj->data);

	/* Select color scheme */
	chk->style &=
		 ~(CHB_STYLE_USE_ALTERNATE_COLORS | CHB_STYLE_TOGGLE_COLORS | CHB_STYLE_NO_BORDERS | CHB_STYLE_NO_FILL);
	chk->state |= CHB_STATE_ALWAYS_REDRAW;

	if(style & CHB_STYLE_NO_BORDERS) {
		chk->style |= CHB_STYLE_NO_BORDERS;
	}

	if(style & CHB_STYLE_NO_FILL) {
		chk->style |= CHB_STYLE_NO_FILL;
	}

	if(style & CHB_STYLE_TOGGLE_COLORS) {
		chk->style |= CHB_STYLE_TOGGLE_COLORS;
	}
	else if(style & CHB_STYLE_USE_ALTERNATE_COLORS) {
		chk->style |= CHB_STYLE_USE_ALTERNATE_COLORS;
	}
	else {
		chk->state &= ~CHB_STATE_ALWAYS_REDRAW;
	}

	/* 3D or 2D */
	if(style & CHB_STYLE_3D) {
		chk->style |= CHB_STYLE_3D;
	}
	else {
		chk->style &= ~CHB_STYLE_3D;
	}

	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetHSpace(UG_WINDOW * wnd, UG_U8 id, UG_S8 hs)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->h_space = hs;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetVSpace(UG_WINDOW * wnd, UG_U8 id, UG_S8 vs)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->v_space = vs;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_CheckboxSetAlignment(UG_WINDOW * wnd, UG_U8 id, UG_U8 align)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	btn = (UG_CHECKBOX *) (obj->data);
	btn->align = align;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_U8 UG_CheckboxGetChecked(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_U8 c = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		c = btn->checked;
	}

	return c;
}


UG_COLOR UG_CheckboxGetForeColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		c = btn->fc;
	}

	return c;
}


UG_COLOR UG_CheckboxGetBackColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		c = btn->bc;
	}

	return c;
}


UG_COLOR UG_CheckboxGetAlternateForeColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		c = btn->afc;
	}

	return c;
}


UG_COLOR UG_CheckboxGetAlternateBackColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		c = btn->abc;
	}

	return c;
}


char * UG_CheckboxGetText(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	char * str = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		str = btn->str;
	}

	return str;
}


UG_FONT * UG_CheckboxGetFont(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_FONT * font = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		font = (UG_FONT *)
		btn->font;
	}

	return font;
}


UG_U8 UG_CheckboxGetStyle(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_U8 style = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		style = btn->style;
	}

	return style;
}


UG_S8 UG_CheckboxGetHSpace(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_S8 hs = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		hs = btn->h_space;
	}

	return hs;
}


UG_S8 UG_CheckboxGetVSpace(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_S8 vs = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		vs = btn->v_space;
	}

	return vs;
}


UG_U8 UG_CheckboxGetAlignment(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_CHECKBOX * btn = NULL;
	UG_U8 align = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_CHECKBOX, id);

	if(obj != NULL) {
		btn = (UG_CHECKBOX *) (obj->data);
		align = btn->align;
	}

	return align;
}


/* ======================================================== */
void _UG_CheckboxUpdate(UG_WINDOW * wnd, UG_OBJECT * obj)
{
	UG_CHECKBOX * chb;
	UG_AREA a;
	UG_TEXT txt;
	UG_U8 d;
	UG_U8 d2;

	/* Get object-specific data */
	chb = (UG_CHECKBOX *) (obj->data);

	/* -------------------------------------------------- */
	/* Object touch section 								*/
	/* -------------------------------------------------- */
	if((obj->touch_state & OBJ_TOUCH_STATE_CHANGED)) {
		/* Handle 'click' event */
		if(obj->touch_state & OBJ_TOUCH_STATE_CLICK_ON_OBJECT) {
			obj->event = CHB_EVENT_CLICKED;
			obj->state |= OBJ_STATE_UPDATE;
		}

		/* Is the Checkbox pressed down? */
		if(obj->touch_state & OBJ_TOUCH_STATE_PRESSED_ON_OBJECT) {
			chb->state |= CHB_STATE_PRESSED;
			obj->state |= OBJ_STATE_UPDATE;
			obj->event = OBJ_EVENT_PRESSED;
		}

		/* Can we release the Checkbox? */
		else if(chb->state & CHB_STATE_PRESSED) {
			chb->state &= ~CHB_STATE_PRESSED;
			obj->state |= OBJ_STATE_UPDATE;
			obj->event = OBJ_EVENT_RELEASED;

			chb->checked = !chb->checked;
		}

		obj->touch_state &= ~OBJ_TOUCH_STATE_CHANGED;
	}

	/* -------------------------------------------------- */
	/* Object update section								*/
	/* -------------------------------------------------- */
	if(obj->state & OBJ_STATE_UPDATE) {
		UG_WindowGetArea(wnd, &a);
		obj->a_abs.xs = obj->a_rel.xs + a.xs;
		obj->a_abs.ys = obj->a_rel.ys + a.ys;
		obj->a_abs.xe = obj->a_rel.xe + a.xs;
		obj->a_abs.ye = obj->a_rel.ye + a.ys;

		if(obj->a_abs.ye > wnd->a.ye)
			return;

		if(obj->a_abs.xe > wnd->a.xe)
			return;

		if(obj->state & OBJ_STATE_VISIBLE) {
			/* 3D or 2D style? */
			d = (chb->style & CHB_STYLE_3D) ? 3: 1;
			d2 = (chb->font->char_width < chb->font->char_height) ? chb->font->char_height: chb->font->char_width;

			/* Full redraw necessary? */
			if((obj->state & OBJ_STATE_REDRAW) || (chb->state & CHB_STATE_ALWAYS_REDRAW)) {
#ifdef USE_PRERENDER_EVENT
				_UG_SendObjectPrerenderEvent(wnd, obj);
#endif

				txt.bc = chb->bc;
				txt.fc = chb->fc;

				if(chb->state & CHB_STATE_PRESSED) {
					/* "toggle" style? */
					if(chb->style & CHB_STYLE_TOGGLE_COLORS) {
						/* Swap colors */
						txt.bc = chb->fc;
						txt.fc = chb->bc;
					}

					/* Use alternate colors? */
					else if(chb->style & CHB_STYLE_USE_ALTERNATE_COLORS) {
						txt.bc = chb->abc;
						txt.fc = chb->afc;
					}
				}

				if(! (chb->style & CHB_STYLE_NO_FILL))
					UG_FillFrame(obj->a_abs.xs + d, obj->a_abs.ys + d, obj->a_abs.xe - d, obj->a_abs.ye - d, txt.bc);

				/* Draw Checkbox text */
				//				 txt.a.xs = obj->a_abs.xs+d;
				//				 txt.a.ys = obj->a_abs.ys+d;
				//				 txt.a.xe = obj->a_abs.xe-d;
				//				 txt.a.ye = obj->a_abs.ye-d;
				txt.a.xs = obj->a_abs.xs + d2 + 3 * d;
				txt.a.ys = obj->a_abs.ys + d;
				txt.a.xe = obj->a_abs.xe;
				txt.a.ye = obj->a_abs.ye;
				txt.align = chb->align;
				txt.font = chb->font;
				txt.h_space = 2;
				txt.v_space = 2;
				txt.str = chb->str;
				_UG_PutText(&txt);
				obj->state &= ~OBJ_STATE_REDRAW;

#ifdef USE_POSTRENDER_EVENT
				_UG_SendObjectPostrenderEvent(wnd, obj);
#endif
			}

			/* Draw Checkbox X */
			if(chb->checked) {
				UG_DrawLine(obj->a_abs.xs + d + 1, obj->a_abs.ys + d, obj->a_abs.xs + d2 + d - 1, 
					obj->a_abs.ys + d2 + d - 2, chb->fc);
				UG_DrawLine(obj->a_abs.xs + d, obj->a_abs.ys + d, obj->a_abs.xs + d2 + d - 1, 
					obj->a_abs.ys + d2 + d - 1, chb->fc);
				UG_DrawLine(obj->a_abs.xs + d, obj->a_abs.ys + d + 1, obj->a_abs.xs + d2 + d - 2, 
					obj->a_abs.ys + d2 + d - 1, chb->fc);

				UG_DrawLine(obj->a_abs.xs + d2 + d - 1, obj->a_abs.ys + d + 1, obj->a_abs.xs + d + 1, 
					obj->a_abs.ys + d2 + d - 1, chb->fc);
				UG_DrawLine(obj->a_abs.xs + d2 + d - 1, obj->a_abs.ys + d, obj->a_abs.xs + d, 
					obj->a_abs.ys + d2 + d - 1, chb->fc);
				UG_DrawLine(obj->a_abs.xs + d2 + d - 2, obj->a_abs.ys + d, obj->a_abs.xs + d, 
					obj->a_abs.ys + d2 + d - 2, chb->fc);
			}
			else {
				UG_DrawLine(obj->a_abs.xs + d + 1, obj->a_abs.ys + d, obj->a_abs.xs + d2 + d - 1, 
					obj->a_abs.ys + d2 + d - 2, chb->bc);
				UG_DrawLine(obj->a_abs.xs + d, obj->a_abs.ys + d, obj->a_abs.xs + d2 + d - 1, 
					obj->a_abs.ys + d2 + d - 1, chb->bc);
				UG_DrawLine(obj->a_abs.xs + d, obj->a_abs.ys + d + 1, obj->a_abs.xs + d2 + d - 2, 
					obj->a_abs.ys + d2 + d - 1, chb->bc);

				UG_DrawLine(obj->a_abs.xs + d2 + d - 1, obj->a_abs.ys + d + 1, obj->a_abs.xs + d + 1, 
					obj->a_abs.ys + d2 + d - 1, chb->bc);
				UG_DrawLine(obj->a_abs.xs + d2 + d - 1, obj->a_abs.ys + d, obj->a_abs.xs + d, 
					obj->a_abs.ys + d2 + d - 1, chb->bc);
				UG_DrawLine(obj->a_abs.xs + d2 + d - 2, obj->a_abs.ys + d, obj->a_abs.xs + d, 
					obj->a_abs.ys + d2 + d - 2, chb->bc);
			}

			/* Draw Checkbox frame */
			if(! (chb->style & CHB_STYLE_NO_BORDERS)) {
				if(chb->style & CHB_STYLE_3D) { /* 3D */
					_UG_DrawObjectFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xs + d2 + 2 * d - 1, 
						obj->a_abs.ys + d2 + 2 * d - 1, 
						(chb->state & CHB_STATE_PRESSED) ? (UG_COLOR *) pal_checkbox_pressed: (UG_COLOR *) pal_checkbox_released);
				}
				else { /* 2D */
					UG_DrawFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xs + d2 + 2 * d - 1, 
						obj->a_abs.ys + d2 + 2 * d - 1, (chb->state & CHB_STATE_PRESSED) ? chb->abc: chb->afc);
				}
			}
		}
		else {
			if(! (chb->style & CHB_STYLE_NO_FILL))
				UG_FillFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xe, obj->a_abs.ye, wnd->bc);
		}

		obj->state &= ~OBJ_STATE_UPDATE;
	}
}


/* -------------------------------------------------------------------------------- */
/* -- TEXTBOX FUNCTIONS 														 -- */
/* -------------------------------------------------------------------------------- */
UG_RESULT UG_TextboxCreate(UG_WINDOW * wnd, UG_TEXTBOX * txb, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye)
{
	UG_OBJECT * obj;

	obj = _UG_GetFreeObject(wnd);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	/* Initialize object-specific parameters */
	txb->str = NULL;
	txb->font = &g_ugui->font;
	txb->style = 0; 								/* reserved */
	txb->fc = wnd->fc;
	txb->bc = wnd->bc;
	txb->align = ALIGN_CENTER;
	txb->h_space = 0;
	txb->v_space = 0;

	/* Initialize standard object parameters */
	obj->update = _UG_TextboxUpdate;
	obj->touch_state = OBJ_TOUCH_STATE_INIT;
	obj->type = OBJ_TYPE_TEXTBOX;
	obj->event = OBJ_EVENT_NONE;
	obj->a_rel.xs = xs;
	obj->a_rel.ys = ys;
	obj->a_rel.xe = xe;
	obj->a_rel.ye = ye;
	obj->a_abs.xs = -1;
	obj->a_abs.ys = -1;
	obj->a_abs.xe = -1;
	obj->a_abs.ye = -1;
	obj->id = id;
	obj->state |= OBJ_STATE_VISIBLE | OBJ_STATE_REDRAW | OBJ_STATE_VALID;
	obj->data = (void *)
	txb;

	/* Update function: Do your thing! */
	obj->state &= ~OBJ_STATE_FREE;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxDelete(UG_WINDOW * wnd, UG_U8 id)
{
	return _UG_DeleteObject(wnd, OBJ_TYPE_TEXTBOX, id);
}


UG_RESULT UG_TextboxShow(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	obj->state |= OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxHide(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	obj->state &= ~OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxSetForeColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR fc)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	txb = (UG_TEXTBOX *) (obj->data);
	txb->fc = fc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxSetBackColor(UG_WINDOW * wnd, UG_U8 id, UG_COLOR bc)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	txb = (UG_TEXTBOX *) (obj->data);
	txb->bc = bc;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxSetText(UG_WINDOW * wnd, UG_U8 id, char * str)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	txb = (UG_TEXTBOX *) (obj->data);
	txb->str = str;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxSetFont(UG_WINDOW * wnd, UG_U8 id, const UG_FONT * font)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	txb = (UG_TEXTBOX *) (obj->data);
	txb->font = font;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxSetHSpace(UG_WINDOW * wnd, UG_U8 id, UG_S8 hs)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	txb = (UG_TEXTBOX *) (obj->data);
	txb->h_space = hs;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxSetVSpace(UG_WINDOW * wnd, UG_U8 id, UG_S8 vs)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	txb = (UG_TEXTBOX *) (obj->data);
	txb->v_space = vs;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_RESULT UG_TextboxSetAlignment(UG_WINDOW * wnd, UG_U8 id, UG_U8 align)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	txb = (UG_TEXTBOX *) (obj->data);
	txb->align = align;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


UG_COLOR UG_TextboxGetForeColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj != NULL) {
		txb = (UG_TEXTBOX *) (obj->data);
		c = txb->fc;
	}

	return c;
}


UG_COLOR UG_TextboxGetBackColor(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;
	UG_COLOR c = C_BLACK;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj != NULL) {
		txb = (UG_TEXTBOX *) (obj->data);
		c = txb->bc;
	}

	return c;
}


char * UG_TextboxGetText(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;
	char * str = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj != NULL) {
		txb = (UG_TEXTBOX *) (obj->data);
		str = txb->str;
	}

	return str;
}


UG_FONT * UG_TextboxGetFont(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;
	UG_FONT * font = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj != NULL) {
		txb = (UG_TEXTBOX *) (obj->data);
		font = (UG_FONT *)
		txb->font;
	}

	return font;
}


UG_S8 UG_TextboxGetHSpace(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;
	UG_S8 hs = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj != NULL) {
		txb = (UG_TEXTBOX *) (obj->data);
		hs = txb->h_space;
	}

	return hs;
}


UG_S8 UG_TextboxGetVSpace(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;
	UG_S8 vs = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj != NULL) {
		txb = (UG_TEXTBOX *) (obj->data);
		vs = txb->v_space;
	}

	return vs;
}


UG_U8 UG_TextboxGetAlignment(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;
	UG_TEXTBOX * txb = NULL;
	UG_U8 align = 0;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_TEXTBOX, id);

	if(obj != NULL) {
		txb = (UG_TEXTBOX *) (obj->data);
		align = txb->align;
	}

	return align;
}


void _UG_TextboxUpdate(UG_WINDOW * wnd, UG_OBJECT * obj)
{
	UG_TEXTBOX * txb;
	UG_AREA a;
	UG_TEXT txt;

	/* Get object-specific data */
	txb = (UG_TEXTBOX *) (obj->data);

	/* -------------------------------------------------- */
	/* Object touch section 								*/
	/* -------------------------------------------------- */

	/* Textbox doesn't support touch */

	/* -------------------------------------------------- */
	/* Object update section								*/
	/* -------------------------------------------------- */
	if(obj->state & OBJ_STATE_UPDATE) {
		if(obj->state & OBJ_STATE_VISIBLE) {
			/* Full redraw necessary? */
			if(obj->state & OBJ_STATE_REDRAW) {
				UG_WindowGetArea(wnd, &a);
				obj->a_abs.xs = obj->a_rel.xs + a.xs;
				obj->a_abs.ys = obj->a_rel.ys + a.ys;
				obj->a_abs.xe = obj->a_rel.xe + a.xs;
				obj->a_abs.ye = obj->a_rel.ye + a.ys;

				if(obj->a_abs.ye >= wnd->a.ye)
					return;

				if(obj->a_abs.xe >= wnd->a.xe)
					return;

#ifdef USE_PRERENDER_EVENT
				_UG_SendObjectPrerenderEvent(wnd, obj);
#endif

				txt.bc = txb->bc;
				txt.fc = txb->fc;

				UG_FillFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xe, obj->a_abs.ye, txt.bc);

				/* Draw Textbox text */
				txt.a.xs = obj->a_abs.xs;
				txt.a.ys = obj->a_abs.ys;
				txt.a.xe = obj->a_abs.xe;
				txt.a.ye = obj->a_abs.ye;
				txt.align = txb->align;
				txt.font = txb->font;
				txt.h_space = txb->h_space;
				txt.v_space = txb->v_space;
				txt.str = txb->str;
				_UG_PutText(&txt);
				obj->state &= ~OBJ_STATE_REDRAW;

#ifdef USE_POSTRENDER_EVENT
				_UG_SendObjectPostrenderEvent(wnd, obj);
#endif
			}
		}
		else {
			UG_FillFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xe, obj->a_abs.ye, wnd->bc);
		}

		obj->state &= ~OBJ_STATE_UPDATE;
	}
}


/* -------------------------------------------------------------------------------- */
/* -- IMAGE FUNCTIONS															 -- */
/* -------------------------------------------------------------------------------- */
UG_RESULT UG_ImageCreate(UG_WINDOW * wnd, UG_IMAGE * img, UG_U8 id, UG_S16 xs, UG_S16 ys, UG_S16 xe, UG_S16 ye)
{
	UG_OBJECT * obj;

	obj = _UG_GetFreeObject(wnd);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	/* Initialize object-specific parameters */
	img->img = NULL;
	img->effect = 0;
	img->type = IMG_TYPE_BMP;

	/* Initialize standard object parameters */
	obj->update = _UG_ImageUpdate;
	obj->touch_state = OBJ_TOUCH_STATE_INIT;
	obj->type = OBJ_TYPE_IMAGE;
	obj->event = OBJ_EVENT_NONE;
	obj->a_rel.xs = xs;
	obj->a_rel.ys = ys;
	obj->a_rel.xe = xe;
	obj->a_rel.ye = ye;
	obj->a_abs.xs = -1;
	obj->a_abs.ys = -1;
	obj->a_abs.xe = -1;
	obj->a_abs.ye = -1;
	obj->id = id;
	obj->state = OBJ_STATE_INIT;
	obj->data = (void *)
	img;

	/* Update function: Do your thing! */
	obj->state &= ~OBJ_STATE_FREE;

	return UG_RESULT_OK;
}


UG_RESULT UG_ImageDelete(UG_WINDOW * wnd, UG_U8 id)
{
	return _UG_DeleteObject(wnd, OBJ_TYPE_IMAGE, id);
}


UG_RESULT UG_ImageShow(UG_WINDOW * wnd, UG_U8 id, UG_U8 effect)
{
	UG_IMAGE * img;
	UG_OBJECT * obj = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_IMAGE, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	obj->state |= OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	/* Get object-specific data */
	img = (UG_IMAGE *) (obj->data);
	img->effect = effect;
	img->draw_over = false;

	return UG_RESULT_OK;
}


UG_RESULT UG_ImageHide(UG_WINDOW * wnd, UG_U8 id)
{
	UG_OBJECT * obj = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_IMAGE, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	obj->state &= ~OBJ_STATE_VISIBLE;
	obj->state |= OBJ_STATE_UPDATE;

	return UG_RESULT_OK;
}


UG_RESULT UG_ImageSetBMP(UG_WINDOW * wnd, UG_U8 id, const UG_BMP * bmp)
{
	UG_OBJECT * obj = NULL;
	UG_IMAGE * img = NULL;

	obj = _UG_SearchObject(wnd, OBJ_TYPE_IMAGE, id);

	if(obj == NULL)
		return UG_RESULT_FAIL;

	img = (UG_IMAGE *) (obj->data);
	img->img = (void *)
	bmp;
	img->type = IMG_TYPE_BMP;
	obj->state |= OBJ_STATE_UPDATE | OBJ_STATE_REDRAW;

	return UG_RESULT_OK;
}


void _UG_ImageUpdate(UG_WINDOW * wnd, UG_OBJECT * obj)
{
	UG_IMAGE * img;
	UG_AREA a;

	/* Get object-specific data */
	img = (UG_IMAGE *) (obj->data);

	printf("update img:%x\r\n", obj->state);

	/* -------------------------------------------------- */
	/* Object touch section 								*/
	/* -------------------------------------------------- */

	/* Image doesn't support touch */

	/* -------------------------------------------------- */
	/* Object update section								*/
	/* -------------------------------------------------- */
	if(obj->state & OBJ_STATE_UPDATE) {
		if(obj->state & OBJ_STATE_VISIBLE) {
			/* Full redraw necessary? */
			if(obj->state & OBJ_STATE_REDRAW) {
				UG_WindowGetArea(wnd, &a);

				/* ToDo: more/better image features */
				obj->a_abs.xs = obj->a_rel.xs + a.xs;
				obj->a_abs.ys = obj->a_rel.ys + a.ys;
				obj->a_abs.xe = obj->a_rel.xs + ((UG_BMP *) img->img)->width + a.xs - 1;
				obj->a_abs.ye = obj->a_rel.ys + ((UG_BMP *) img->img)->height + a.ys - 1;

				if(obj->a_abs.ye > wnd->a.ye) {
					printf("--1:%u %u\r\n", obj->a_abs.ye, wnd->a.ye);
					return;
				}

				if(obj->a_abs.xe > wnd->a.xe) {
					printf("--2:%u %u\r\n", obj->a_abs.xe, wnd->a.xe);
					return;
				}

				/* Draw Image */
				if((img->img != NULL) && (img->type & IMG_TYPE_BMP)) {
					img->draw_over = draw_bmp_driver(obj->a_abs.xs, obj->a_abs.ys, (UG_BMP *) img->img, img->effect);
				}
				else {
					// other img
					img->draw_over = true;
				}

				if((img->effect == 0) || img->draw_over)
					obj->state &= ~OBJ_STATE_REDRAW;
			}
		}
		else {
			UG_FillFrame(obj->a_abs.xs, obj->a_abs.ys, obj->a_abs.xe, obj->a_abs.ye, wnd->bc);
		}

		if((img->effect == 0) || img->draw_over)
			obj->state &= ~OBJ_STATE_UPDATE;
		else
   			g_ugui->active_window->state |= WND_STATE_UPDATE;
	}
}


